﻿using System;

namespace Aeon.Emulator.Sound.FM.Operators
{
    internal abstract class OperatorWaveform
    {
        private const double TwoPI = 2 * Math.PI;
        private const double FourPI = 4 * Math.PI;

        private static readonly OperatorWaveform[] Waveforms =
        {
            new SineWaveform(),
            new FirstHalfSineWaveform(),
            new AbsSineWaveform(),
            new FirstAndThirdSineWaveform(),
            new DoubleFrequencySineWaveform(),
            new FirstHalfDoubleFrequencySineWaveform(),
            new SquareWaveform(),
            new ExponentialWaveform()
        };

        public static OperatorWaveform GetWaveform(int index)
        {
            return Waveforms[index];
        }

        public abstract double GetValue(double percent);

        private sealed class SineWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                return Math.Sin(percent * TwoPI);
            }
        }

        private sealed class FirstHalfSineWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                if(percent < 0.5)
                    return Math.Sin(percent * TwoPI);
                else
                    return 0;
            }
        }

        private sealed class AbsSineWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                return Math.Abs(Math.Sin(percent * TwoPI));
            }
        }

        private sealed class FirstAndThirdSineWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                if(percent < 0.25)
                    return Math.Sin(percent * TwoPI);
                else if(percent < 0.5)
                    return 0;
                else if(percent < 0.75)
                    return Math.Sin((percent - 0.75) * TwoPI);
                else
                    return 0;
            }
        }

        private sealed class DoubleFrequencySineWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                return Math.Sin(percent * FourPI);
            }
        }

        private sealed class FirstHalfDoubleFrequencySineWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                if(percent < 0.25)
                    return Math.Sin(percent * FourPI);
                else if(percent < 0.5)
                    return Math.Sin((percent - 0.25) * FourPI);
                else
                    return 0;
            }
        }

        private sealed class SquareWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                return percent < 0.5 ? 1 : -1;
            }
        }

        private sealed class ExponentialWaveform : OperatorWaveform
        {
            public override double GetValue(double percent)
            {
                if(percent < 0.5)
                    return Math.Pow(2, -0.0625 * percent);
                else
                    return -Math.Pow(2, -((-0.0625 * (1.0 - percent)) + 0.0625));
            }
        }
    }
}
